home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Shareware Grab Bag
/
Shareware Grab Bag.iso
/
007
/
a86v302a.arc
/
TCOLS.8
< prev
next >
Wrap
Text File
|
1987-04-26
|
18KB
|
407 lines
;---------------
; TCOLS
;---------------
JMP TCOLS
DEFAULT MACRO
DEFT_#1 EQU #2
DB ' (default #2)',0D,0A
#EM
DOC_MSG:
DB 0D,0A
DB 'TCOLS V1.0 (C)1986 Eric Isaacson, 416 E. Univ.St., BMG IN 47401',0D,0A
DB ' Permission to use granted only to registered A86 users.',0D,0A,0D,0A
DB 'TCOLS converts single-column input into paged, multi-column output.',0D,0A
DB 'Usage: TCOLS <in >out #1 #2 #3 #4 #5 , where',0D,0A
DB ' #1 is the number of major columns you want each page split into',0D,0A
DB ' #2 is the number of lines to skip between each page'
DEFAULT SKIPCT,6
DB ' #3 is the number of chars per line the printer is now set to'
DEFAULT PWIDTH,80
DB ' #4 is the number of lines per page the printer is now set to'
DEFAULT LPAGE,66
DB ' #5 is which line within the first page the printer is now at'
DEFAULT LSTART,0
DB 0D,0A
DB 'Examples:',0D,0A
DB 'TCOLS <MYPROG.XRF >PRN 4 6 96 88',0D,0A
DB ' sends the file MYPROG.XRF to the printer, split into 4 columns, where'
DB 0D,0A
DB ' the printer is set to 96 characters a line, 8 lines per inch.',0D,0A
DB 'TCOLS <NARROW.LST 6 0 80 23 22 | MORE',0D,0A
DB ' provides 6-column screen-paged output.',0D,0A
DB 'NOTE for readability, TCOLS will convert underscores to hyphens when the'
DB 0D,0A
DB ' lines per page is greater than 80.',0D,0A
DOC_LEN EQU $-DOC_MSG
DATA SEGMENT
ORG 01000
BUF:
DB 04000 DUP (?)
OUTBUF:
DB 02000 DUP (?)
OUTBUF_LIM:
DB 0100 DUP (?)
DW ? ; in case the line-overflow logic scans back
SOURCE_BUF:
DB 04002 DUP (?)
WIDTH DW ? ; number of characters in an major output column
NCOLS DB ? ; number of major output columns on a page
LPAGE DB ? ; total number of lines on a page (including skipped)
PWIDTH DW ? ; total width in characters of a printed page
BUFEND DW ? ; pointer reached when a buffered page is complete
SKIPCT DB ? ; number of lines skipped between pages
LSTART DB ? ; number of first-page lines already output before program
THISPAGE DB ? ; number of printed lines on this page
DATA ENDS
MAIN:
TCOLS:
CALL SCAN_ARGS ; scan the command arguments
CALL READ_SOURCE ; read the first block of source text
JZ >L2 ; exit if there was no source text
L1: ; loop here for each page of output
CALL GATHER_PAGE ; input a page, and arrange in into columns
PUSHF ; save Z-flag, to see if there is more input
CALL OUTPUT_PAGE ; process the columns-buffer into final output
CALL OFLUSH ; flush the output buffer
POPF ; restore Z flag, is there more output?
JNZ L1 ; loop if there is more output
L2:
JMP GOOD_EXIT ; all done, go back to operating system
; CHECK_DIGIT sees if there is another command-tail argument pointed to by
; SI. If there is, it had better start with a decimal digit, or else we
; abort the program. We return NZ if there is an argument; Z if there are
; no more arguments (terminator 0FF is seen).
CHECK_DIGIT:
PUSH AX ; preserve register across call
L1: ; loop here to skip over blanks and control chars
LODSB ; load the next character
CMP AL,' ' ; is it a blank or control char?
JBE L1 ; loop if yes, to skip the character
DEC SI ; retreat SI back to the first argument-char
CMP AL,0FF ; is it the command-tail terminator?
JE >L2 ; return Z if yes
SUB AL,'0' ; reduce first character to 0--9 range if digit
CMP AL,10 ; is the character a digit?
JAE >E1 ; abort the program if not; NZ is set if yes
L2:
POP AX ; restore clobbered register
RET
E1: ; improper command tail in program invocation
MOV DX,DOC_MSG ; point to the documentation-message
MOV CX,DOC_LEN ; load size of message
JMP ERROR_EXIT ; educate the user about this program
; CLEAR_PAGE calculates the number of lines to be printed on the coming
; page, at sets up a blank columns-buffer based on that number. We return
; with DI pointing just beyond the blanked buffer. If the calculations
; indicate something is wrong, we abort the program.
CLEAR_PAGE:
MOV AL,LPAGE ; fetch the total number of lines on a page
SUB AL,SKIPCT ; subtract the number of lines that we skip
JBE E1 ; abort if we skip more lines than there are
SUB AL,LSTART ; also subtract any first-page lines already output
JBE E1 ; abort if that subtraction has exhausted the count
MOV THISPAGE,AL ; store as the number of lines on this page
MOV LSTART,0 ; cancel first-page count for the subsequent pages
MOV CX,PWIDTH ; load the number of characters per line
MUL CL ; calculate the number of characters in the columns buffer
XCHG CX,AX ; swap the count into CX for blanking
MOV DI,BUF ; point DI to the start of the columns buffer
MOV AL,' ' ; we will fill the buffer with blanks
REP STOSB ; buffer is filled, DI points beyond the buffer
RET
; SCAN_ARGS scans the decimal arguments in the command tail at DS:080. All the
; appropriate page-size variables are set according to these argument values.
SCAN_ARGS:
MOV SI,080 ; point to the command-tail buffer in the PSP
LODSB ; load the size of the command tail
CBW ; extend the size AL to AX
XCHG BX,AX ; swap the size into BX, for indexing
MOV [BX+SI],0FF ; mark the end of the command-tail with terminator 0FF
CALL CHECK_DIGIT ; any there any arguments in the command tail?
JZ E1 ; abort the program if there are not
CALL SCAN_DECIMAL ; input the first argument-- number of major columns
MOV NCOLS,AL ; store it
XCHG BX,AX ; also swap NCOLS into BL for later calculations
MOV AL,DEFT_SKIPCT; load the default number of lines skipped
CALL SCAN_DECIMAL ; read the next argument if there is any
MOV SKIPCT,AL ; store the number of lines to skip between pages
MOV AL,DEFT_PWIDTH; load the default page width
CALL SCAN_DECIMAL ; read the next argument if there is any
MOV CL,AL ; store in CL for the moment
DIV BL ; divide by major columns count, to get chars per column
SUB CL,AH ; subtract remainder from page width, insures even multiple
MOV AH,0 ; zero out the remainder
MOV WIDTH,AX ; store the number of characters in an output column
CMP AL,2 ; this width had better be at least 2 chars
JB E1 ; abort the program if not
MOV AL,CL ; AX is now the characters-per-line
MOV PWIDTH,AX ; store the characters-per-line printer is set to
MOV AX,CX ; re-fetch the characters-per-line
ADD AX,BUF ; calculate beyond-first-line, reached when buffer full
MOV BUFEND,AX ; store the value for later use
MOV AL,DEFT_LPAGE ; load defualt lines-per-page
CALL SCAN_DECIMAL ; read the next argument, if there is any
MOV LPAGE,AL ; store the total number of lines on a printed page
MOV AL,DEFT_LSTART; load the default first-line-position
CALL SCAN_DECIMAL ; read the next argument, if any
MOV LSTART,AL ; store the starting first-page position
RET
; GATHER_PAGE takes input text at DS:SI, and formats it into columns on a
; single page, at BUF. RZ if the input file end (0FFH marker) was seen.
GATHER_PAGE:
CALL CLEAR_PAGE ; blank-fill the columns-buffer
MOV BX,DI ; point BX beyond the columns-buffer
MOV BP,DX,DI,BUF ; start BP=column ptr DX=line ptr DI=char ptr
L1: ; loop here for each column entry
CALL GATHER_LINE ; copy an input line to a column entry
JZ RET ; return if there is no more input
CALL NEXT_LINE ; advance pointers to the next column entry
JB L1 ; loop if there is another entry on this page
OR AL,0FF ; page complete: set NZ to signal more input
RET
; NEXT_LINE sets DI to the next line of this column in the page display buffer.
; It moves to the top of the next column as necessary. RAE if the page is
; full.
NEXT_LINE:
ADD DX,PWIDTH ; advance column-pointer to same column on the next line
CMP DX,BX ; have we run off the end of the buffer?
JB >L1 ; skip if we have not
ADD BX,WIDTH ; one column is complete-- advance end-buffer-limit
ADD BP,WIDTH ; advance the column-pointer to the next column
MOV DX,BP ; set the line-ptr to this new column
CMP BP,BUFEND ; have the columns run off the right end of the page?
JAE RET ; return AE if they have-- page is full of columns
L1:
MOV DI,DX ; set char-output pointer to the new line-ptr value
RET
; GATHER_LINE copies a line of text from SI to DI. RZ if the input file end
; is seen.
GATHER_LINE:
MOV CX,WIDTH ; set up the limit for output in this column-entry
L0: ; loop here for each character output to column-entry
LODSB ; fetch a character from the input
CMP AL,020 ; is it a control-character?
JB >L1 ; jump if yes
CMP AL,0FE ; is it a marker?
JAE >L2 ; jump if yes
CMP AL,'_' ; is it an underscore?
JE >L5 ; jump if yes
L6:
STOSB ; store the character in columns-buffer
LOOP L0 ; loop to look at the next character
DEC SI,2 ; column-entry has overflowed: retreat input pointer
MOV B[SI],0FE ; mark input buffer with line-overflow marker
ES MOV B[DI-1],' '; cancel the last byte output-- it will go to next line
RET
L1: ; control-character seen on the input line
CMP AL,0A ; is it the line-terminator?
JNE L0 ; if not then ignore it
TEST AL,AL ; set NZ to signal end-of-file was not seen
RET
L2: ; a marker was seen in the input stream
JE >L4 ; jump if it was the line-overflow marker
CALL READ_SOURCE ; it was the end-of-file marker: read more input
JNE L0 ; loop for more reading if there was more input
RET ; input exhausted-- return Z to caller
L4: ; a line-overflow marker was seen in the input-stream
PUSH BX,CX,DI ; preserve registers across indentation
MOV AL,0A ; we will look for the linefeed for this line
MOV DI,SI ; transfer source pointer to DI for linefeed-search
REPNE SCASB ; find where the linefeed is
CMP B[DI-2],0D ; was there also a carriage return?
IF E INC CX ; if yes then don't count it
MOV BX,CX ; preserve the count for reducing the stacked CX value
POP DI ; restore columnar-output pointer
MOV AL,' ' ; load blank-- we are indenting to right-justify overflow
REP STOSB ; now the overflow is indented
POP CX ; restore original count
SUB CX,BX ; reduce count by the number of blanks we indented
POP BX ; restore clobbered register
JMP L0
L5: ; an underscore was seen in the input stream
CMP LPAGE,80 ; are we squeezing those lines tightly on a page?
JBE L6 ; jump if not, no need to convert
MOV AL,'-' ; tight squeeze: change to hyphen for readability
JMP L6 ; re-join the main storage-loop
; SCAN_DECIMAL sets AX to the value of the next decimal-number argument
; pointed to by SI, and advances SI beyond the argument. If there are
; no more arguments, we retain the input value AL as our return-value.
SCAN_DECIMAL:
MOV AH,0 ; high-byte of default value is always zero
CALL CHECK_DIGIT ; is there an argument there?
JZ RET ; return the default value if there was not
PUSH BX,DX ; preserve registers across call
SUB BX,BX ; initialize BX, we will accumulate value there
L1: ; loop here for each digit of the number
LODSB ; load the next digit
SUB AL,'0' ; reduce the digit to a binary value
MOV DX,10 ; load multiplicand, it is also the digit-limit value
CMP AL,DL ; is it in fact another decimal digit?
JAE >L2 ; jump if not to exit this procedure
CBW ; extend value AL to AX
XCHG AX,BX ; swap previous accumulation into AX, new digit into BX
MUL DX ; multiply the previous accumulation by 10
ADD BX,AX ; add into the new digit value
JMP L1 ; loop to accumulate another digit
L2: ; non-digit was seen
DEC SI ; retreat back to the non-digit
XCHG AX,BX ; swap the accumulated value into AX
POP DX,BX ; restore clobbered registers
RET
; OUTPUT_PAGE transforms the column-buffer output into final-forn output,
; with CRLFs inserted and trailing blanks removed.
OUTPUT_PAGE:
PUSH CX,DX,SI ; preserve registers across call
MOV DI,OUTBUF ; initialize the output pointer
MOV DX,PWIDTH ; DX holds the count of each columnized line
MOV SI,BUF ; initialize the column-buffer source pointer
MOV CL,THISPAGE ; load the lines count into CL
MOV CH,0 ; extend the CL-count to CX
L1: ; loop here to output each line
CALL OUTPUT_LINE ; output the line
ADD SI,DX ; advance the source pointer to the next columnized line
LOOP L1 ; loop to output the next line
POP SI,DX,CX ; restore clobbered registers
MOV AH,SKIPCT ; load the count of lines to skip
TEST AH,AH ; are there any lines to skip?
JZ RET ; return if not
L2: ; loop here for each skipped line between pages
CALL CRLF_OUT ; output a blank line
DEC AH ; count down lines
JNZ L2 ; loop to output the next blank line
RET
; OUTPUT_LINE transforms a single line of columnized output into its final
; form, with trailing blanks removed and a CRLF appended to the end of the
; line.
OUTPUT_LINE:
PUSH CX,SI ; preserve registers across call
MOV CX,DX ; fetch the count of columnized-line bytes
PUSH DI ; preserve the output pointer
MOV DI,SI ; load DI with source pointer, for scanning
ADD DI,CX ; advance DI beyond this source line
DEC DI ; retreat to the last byte of this source line
MOV AL,' ' ; load blank, to scan for trailing blanks
STD ; scanning will be backwards
REPE SCASB ; scan through the trailing blanks
CLD ; restore forward scanning
IF NE INC CX ; do not include the last byte if it was a non-blank
POP DI ; restore output pointer
REP MOVSB ; CX was decremented by # of trailing blanks; now move line
POP SI,CX ; restore clobbered registers
CRLF_OUT: ; output a CRLF to the end of a line
PUSH AX ; preserve AX across call
MOV AX,0A0D ; load CRLF, backwards according to 8086 storage conventions
STOSW ; output the CRLF
POP AX ; restore clobbered register
CHECK_FLUSH: ; check to see if we need to flush the output buffer
CMP DI,OUTBUF_LIM ; have we reached the buffer limit?
JB RET ; do nothing if we have not reached the limit
OFLUSH: ; flush the output buffer
PUSH AX,BX,CX,DX ; save registers across call
MOV BX,1 ; handle-number for standard output is 1
MOV DX,OUTBUF ; point DX to the output buffer
MOV CX,DI ; point CX beyond the bytes we have output
SUB CX,DX ; calculate the number of bytes output
CALL MWRITE ; send those bytes to standard output
MOV DI,DX ; reset the output-pointer to the start of the buffer
POP DX,CX,BX,AX ; restore clobbered registers
RET
; READ_SOURCE reads from standard input to SOURCE_BUF, and marks the end
; of the received text with 0FF. Return with SI pointing to SOURCE_BUF.
; RZ if there was no more input.
READ_SOURCE:
PUSH AX,BX,CX,DX ; preserve registers across call
SUB BX,BX ; handle-value for standard input is zero
MOV DX,SOURCE_BUF ; point to the destination for our source-read
MOV CX,04000 ; load the size limit for the read
CALL MREAD ; read from standard input into the source buffer
XCHG BX,AX ; swap the actual number of bytes read into BX
MOV SI,DX ; point SI to the bytes that were read
MOV B[BX+SI],0FF ; terminate the bytes with an 0FF marker
TEST BX,BX ; set Z if there were no bytes read
POP DX,CX,BX,AX ; restore clobbered registers
RET
; MREAD reads from the open-file numbered BX, to the CX bytes at DX. Return
; with AX set to the number of bytes actually read.
MREAD:
MOV AH,03FH ; MSDOS function number for READ
MSDOS:
INT 021H ; all MSDOS calls go through this interrupt
RET
; MWRITE writes CX bytes from DX to the open-file numbered BX. Return Carry if
; the write failed, with AX set to an error number.
MWRITE:
MOV AH,040H ; MSDOS function number for WRITE
JMP MSDOS ; jump to call the operating system
; EXIT exits the program back to the invoking process, with a status of AL.
GOOD_EXIT:
MOV AL,0 ; zero status means success
EXIT:
MOV AH,04CH ; MSDOS function number for EXIT
JMP MSDOS ; jump to call the operating system
; ERROR_EXIT writes to the error-console the message of CX bytes at DS:DS;
; then exits the program with a status of 1.
ERROR_EXIT:
MOV BX,2 ; handle-value for error-console is 2
CALL MWRITE ; write message to the error-console
MOV AL,1 ; 1-code means failure
JMP EXIT ; go back to operating system